import os
from PySide6.QtWidgets import (
    QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QTreeWidget, QTreeWidgetItem, QHeaderView, QLabel,
    QTableWidget, QTableWidgetItem, QTreeWidgetItemIterator, QLineEdit,
    QGroupBox, QTextEdit, QRadioButton, QButtonGroup, QCheckBox,
    QSplitter, QComboBox, QDialog, QDialogButtonBox, QFileDialog,
    QToolButton, QMenu
)
from PySide6.QtCore import Signal, Qt, QSize, QPoint

class CharacteristicControlWidget(QWidget):
    """
    单个特征的交互控件.
    """
    read_requested = Signal(str)
    write_requested = Signal(str, bytes)
    notify_toggled = Signal(str, bool)

    def __init__(self, char_data, parent=None):
        super().__init__(parent)
        self.uuid = char_data["uuid"]
        self.properties = char_data["properties"]
        self._init_ui()

    def _init_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(0, 0, 0, 0)
        
        group_box = QGroupBox("交互控制") # 修改标题, 避免UUID重复
        main_layout.addWidget(group_box)
        
        layout = QVBoxLayout(group_box)
        
        # 属性标签不再需要, 因为会在主树中显示
        # properties_label = QLabel(f"Properties: {', '.join(self.properties)}")
        # properties_label.setWordWrap(True)
        # layout.addWidget(properties_label)

        # 接收数据显示
        if "read" in self.properties or "notify" in self.properties or "indicate" in self.properties:
            receive_group = QGroupBox("接收")
            receive_layout = QVBoxLayout(receive_group)
            self.received_data_text = QTextEdit()
            self.received_data_text.setReadOnly(True)
            self.received_data_text.setFixedHeight(120) # 增加高度
            
            # 接收格式与清空按钮
            receive_options_layout = QHBoxLayout()

            # 接收格式选择
            self.receive_format_group = QButtonGroup(self)
            receive_hex_radio = QRadioButton("Hex")
            receive_ascii_radio = QRadioButton("ASCII")
            receive_hex_radio.setChecked(True)
            self.receive_format_group.addButton(receive_hex_radio, 0)
            self.receive_format_group.addButton(receive_ascii_radio, 1)
            
            receive_options_layout.addWidget(receive_hex_radio)
            receive_options_layout.addWidget(receive_ascii_radio)
            receive_options_layout.addStretch()

            # 清空按钮
            clear_receive_button = QPushButton("清空")
            clear_receive_button.clicked.connect(self._on_clear_received)
            receive_options_layout.addWidget(clear_receive_button)


            receive_layout.addWidget(self.received_data_text)
            receive_layout.addLayout(receive_options_layout)
            layout.addWidget(receive_group)

        # 功能按钮
        buttons_layout = QHBoxLayout()
        if "read" in self.properties:
            read_button = QPushButton("读取")
            read_button.clicked.connect(self._on_read)
            buttons_layout.addWidget(read_button)
        
        if "notify" in self.properties or "indicate" in self.properties:
            self.notify_checkbox = QCheckBox("通知")
            self.notify_checkbox.toggled.connect(self._on_notify_toggled)
            buttons_layout.addWidget(self.notify_checkbox)
            
        buttons_layout.addStretch()
        layout.addLayout(buttons_layout)

        # 发送数据
        if "write" in self.properties or "write-without-response" in self.properties:
            send_group = QGroupBox("发送")
            send_layout = QVBoxLayout(send_group)
            
            # 新增: 发送历史记录
            history_group = QGroupBox("发送历史")
            history_layout = QVBoxLayout(history_group)
            self.sent_data_history_text = QTextEdit()
            self.sent_data_history_text.setReadOnly(True)
            self.sent_data_history_text.setFixedHeight(80) # 设置一个合适的高度
            
            clear_history_button = QPushButton("清空历史")
            clear_history_button.clicked.connect(self._on_clear_history)
            
            history_top_layout = QHBoxLayout()
            history_top_layout.addStretch()
            history_top_layout.addWidget(clear_history_button)

            history_layout.addLayout(history_top_layout)
            history_layout.addWidget(self.sent_data_history_text)
            
            send_layout.addWidget(history_group)

            self.send_data_input = QLineEdit()
            self.send_data_input.setPlaceholderText("在此输入要发送的内容")
            
            # 发送格式选择
            self.send_format_group = QButtonGroup(self)
            send_hex_radio = QRadioButton("Hex")
            send_ascii_radio = QRadioButton("ASCII")
            send_hex_radio.setChecked(True)
            self.send_format_group.addButton(send_hex_radio, 0)
            self.send_format_group.addButton(send_ascii_radio, 1)

            send_format_layout = QHBoxLayout()
            send_format_layout.addWidget(send_hex_radio)
            send_format_layout.addWidget(send_ascii_radio)
            send_format_layout.addStretch()

            send_button = QPushButton("发送")
            send_button.clicked.connect(self._on_send)
            
            send_controls_layout = QHBoxLayout()
            send_controls_layout.addWidget(self.send_data_input, 1) # 让输入框占据更多空间
            send_controls_layout.addWidget(send_button)

            send_layout.addLayout(send_controls_layout)
            send_layout.addLayout(send_format_layout)
            layout.addWidget(send_group)

    def _on_clear_received(self):
        self.received_data_text.clear()

    def _on_clear_history(self):
        self.sent_data_history_text.clear()

    def _on_read(self):
        self.read_requested.emit(self.uuid)

    def _on_send(self):
        text = self.send_data_input.text()
        if not text:
            return

        try:
            if self.send_format_group.checkedId() == 0: # Hex
                # 移除空格并转换
                data = bytes.fromhex(text.replace(" ", ""))
            else: # ASCII
                data = text.encode('utf-8')
            self.write_requested.emit(self.uuid, data)
            # 发送成功后记录到历史，手动输入发送的数据无需箭头标识
            self.sent_data_history_text.append(text)
        except ValueError:
            # 可以在这里加一个错误提示，比如弹窗或状态栏信息
            print(f"无效的Hex输入: {text}")

    def _on_notify_toggled(self, checked):
        self.notify_toggled.emit(self.uuid, checked)

    def update_value(self, value: bytes):
        if self.receive_format_group.checkedId() == 0: # Hex
            self.received_data_text.append(value.hex(' '))
        else: # ASCII
            try:
                self.received_data_text.append(value.decode('utf-8'))
            except UnicodeDecodeError:
                self.received_data_text.append(f"[Invalid ASCII]: {value.hex(' ')}")


class DeviceControlWidget(QWidget):
    """
    包含连接和断开按钮的控件.
    """
    connect_clicked = Signal(str)
    disconnect_clicked = Signal(str)
    # test_signal_clicked 不再需要
    # test_signal_clicked = Signal(str)

    def __init__(self, address, parent=None):
        super().__init__(parent)
        self.address = address
        
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        self.connect_button = QPushButton("连接")
        self.disconnect_button = QPushButton("断开")
        # self.test_button = QPushButton("信号测试") # 移除测试按钮

        self.disconnect_button.setEnabled(False) # 初始时不可用

        layout.addWidget(self.connect_button)
        layout.addWidget(self.disconnect_button)
        # layout.addWidget(self.test_button) # 移除测试按钮

        self.connect_button.clicked.connect(lambda: self.connect_clicked.emit(self.address))
        self.disconnect_button.clicked.connect(lambda: self.disconnect_clicked.emit(self.address))
        # self.test_button.clicked.connect(lambda: self.test_signal_clicked.emit(self.address)) # 移除信号连接

class NewProtocolDialog(QDialog):
    """新建协议对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("新建协议")
        self.resize(400, 150)
        
        layout = QVBoxLayout(self)
        
        # 协议名称
        name_layout = QHBoxLayout()
        name_layout.addWidget(QLabel("协议名称:"))
        self.name_edit = QLineEdit()
        name_layout.addWidget(self.name_edit)
        layout.addLayout(name_layout)
        
        # 文件路径
        path_layout = QHBoxLayout()
        path_layout.addWidget(QLabel("保存路径:"))
        self.path_edit = QLineEdit()
        self.path_edit.setText("protocols/protocol.json")  # 默认保存到protocols文件夹
        path_layout.addWidget(self.path_edit, 1)
        self.browse_button = QPushButton("浏览...")
        self.browse_button.clicked.connect(self.browse_path)
        path_layout.addWidget(self.browse_button)
        layout.addLayout(path_layout)
        
        # 按钮
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
    
    def browse_path(self):
        """浏览文件保存路径"""
        # 确保protocols文件夹存在
        os.makedirs("protocols", exist_ok=True)
        
        path, _ = QFileDialog.getSaveFileName(
            self, "保存协议文件", "protocols", "JSON文件 (*.json)"
        )
        if path:
            # 确保文件保存在protocols文件夹中
            if not path.startswith("protocols/") and not path.startswith("protocols\\"):
                path = os.path.join("protocols", os.path.basename(path))
            self.path_edit.setText(path)
    
    def get_protocol_info(self):
        """获取协议信息"""
        return {
            "name": self.name_edit.text(),
            "path": self.path_edit.text()
        }


class AddComponentDialog(QDialog):
    """添加组件对话框"""
    def __init__(self, comp_type, parent=None):
        super().__init__(parent)
        self.comp_type = comp_type
        
        if comp_type == "button":
            self.setWindowTitle("添加按钮")
        elif comp_type == "text_display":
            self.setWindowTitle("添加文本框")
        elif comp_type == "slider":
            self.setWindowTitle("添加滑动条")
        
        layout = QVBoxLayout(self)
        
        # 组件ID
        id_layout = QHBoxLayout()
        id_layout.addWidget(QLabel("组件ID:"))
        self.id_edit = QLineEdit()
        id_layout.addWidget(self.id_edit)
        layout.addLayout(id_layout)
        
        # 按钮特有设置
        if comp_type == "button":
            # 按钮文本
            text_layout = QHBoxLayout()
            text_layout.addWidget(QLabel("按钮文本:"))
            self.text_edit = QLineEdit("新按钮")
            text_layout.addWidget(self.text_edit)
            layout.addLayout(text_layout)
        
        # 文本框特有设置
        elif comp_type == "text_display":
            # 初始文本
            text_layout = QHBoxLayout()
            text_layout.addWidget(QLabel("初始文本:"))
            self.text_edit = QLineEdit("文本显示")
            text_layout.addWidget(self.text_edit)
            layout.addLayout(text_layout)
        
        # 滑动条特有设置
        elif comp_type == "slider":
            # 滑动条标签
            label_layout = QHBoxLayout()
            label_layout.addWidget(QLabel("标签文本:"))
            self.label_edit = QLineEdit("参数调节")
            label_layout.addWidget(self.label_edit)
            layout.addLayout(label_layout)
            
            # 最小值
            min_layout = QHBoxLayout()
            min_layout.addWidget(QLabel("最小值:"))
            self.min_edit = QLineEdit("0")
            min_layout.addWidget(self.min_edit)
            layout.addLayout(min_layout)
            
            # 最大值
            max_layout = QHBoxLayout()
            max_layout.addWidget(QLabel("最大值:"))
            self.max_edit = QLineEdit("100")
            max_layout.addWidget(self.max_edit)
            layout.addLayout(max_layout)
            
            # 默认值
            default_layout = QHBoxLayout()
            default_layout.addWidget(QLabel("默认值:"))
            self.default_edit = QLineEdit("50")
            default_layout.addWidget(self.default_edit)
            layout.addLayout(default_layout)
            
            # 单位
            unit_layout = QHBoxLayout()
            unit_layout.addWidget(QLabel("单位:"))
            self.unit_edit = QLineEdit("")
            self.unit_edit.setPlaceholderText("例如: mm, °C, kg等")
            unit_layout.addWidget(self.unit_edit)
            layout.addLayout(unit_layout)
        
        # 按钮
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
    
    def get_component_info(self):
        """获取组件信息"""
        info = {
            "id": self.id_edit.text(),
            "type": self.comp_type
        }
        
        if self.comp_type == "button":
            info["text"] = self.text_edit.text()
        elif self.comp_type == "text_display":
            info["text"] = self.text_edit.text()
        elif self.comp_type == "slider":
            info["label"] = self.label_edit.text()
            try:
                info["min"] = int(self.min_edit.text())
                info["max"] = int(self.max_edit.text())
                info["default"] = int(self.default_edit.text())
            except ValueError:
                # 使用默认值
                info["min"] = 0
                info["max"] = 100
                info["default"] = 50
            info["unit"] = self.unit_edit.text()
        
        return info


class MainWindow(QMainWindow):
    """
    应用程序的主窗口.
    """
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("BLE 上位机")
        self.setGeometry(100, 100, 1200, 700)

        # 全局状态栏
        self.status_label = QLabel("无蓝牙连接")
        self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        
        # Main layout
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 使用一个垂直布局来包含状态栏和主水平布局
        root_layout = QVBoxLayout(central_widget)
        root_layout.addWidget(self.status_label)

        main_layout = QHBoxLayout()
        root_layout.addLayout(main_layout)

        # Left panel for scanning and devices
        left_panel = QWidget()
        left_layout = QVBoxLayout(left_panel)
        
        self.scan_button = QPushButton("扫描设备")
        
        # 添加名称筛选输入框
        self.filter_input = QLineEdit()
        self.filter_input.setPlaceholderText("按名称筛选...")
        
        self.device_tree = QTreeWidget()
        self.device_tree.setHeaderLabels(["设备信息", "MAC 地址", "操作"])
        self.device_tree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
        self.device_tree.header().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
        self.device_tree.header().setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)


        left_layout.addWidget(self.scan_button)
        left_layout.addWidget(self.filter_input) # 将输入框添加到布局
        left_layout.addWidget(self.device_tree)

        # Right panel for services and characteristics
        right_panel = QWidget()
        right_layout = QVBoxLayout(right_panel)
        
        # 创建垂直分割器，用于调整服务与特征部分和协议测试部分的高度
        self.right_splitter = QSplitter(Qt.Orientation.Vertical)
        right_layout.addWidget(self.right_splitter)
        
        # 服务与特征部分
        services_widget = QWidget()
        services_layout = QVBoxLayout(services_widget)
        services_layout.setContentsMargins(0, 0, 0, 0)
        
        services_layout.addWidget(QLabel("服务与特征"))
        self.service_tree = QTreeWidget()
        self.service_tree.setHeaderLabels(["服务/特征", "UUID", "属性"])
        # 确保最后一列不自动拉伸, 优先保证UUID列的空间
        self.service_tree.header().setStretchLastSection(False)
        # 调整列宽策略
        self.service_tree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
        self.service_tree.header().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        self.service_tree.header().setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
        
        services_layout.addWidget(self.service_tree)
        
        # 协议测试部分
        protocol_widget = QWidget()
        protocol_layout = QVBoxLayout(protocol_widget)
        protocol_layout.setContentsMargins(0, 0, 0, 0)
        
        # 协议测试标题栏
        protocol_header = QHBoxLayout()
        protocol_header.addWidget(QLabel("协议测试"))
        
        # 添加操作按钮区域
        self.add_btn = QToolButton()
        self.add_btn.setText("+")
        self.add_btn.setToolTip("添加组件")
        
        # 添加组件下拉菜单
        self.add_menu = QMenu()
        self.add_btn_action = self.add_menu.addAction("添加按钮")
        self.add_txt_action = self.add_menu.addAction("添加文本框")
        self.add_slider_action = self.add_menu.addAction("添加滑动条")
        self.add_btn.setMenu(self.add_menu)
        self.add_btn.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
        
        self.save_btn = QPushButton("保存")
        self.save_btn.setToolTip("保存协议配置")
        
        self.lock_all_btn = QPushButton("锁定所有")
        self.lock_all_btn.setToolTip("锁定所有组件")
        
        self.unlock_all_btn = QPushButton("解锁所有")
        self.unlock_all_btn.setToolTip("解锁所有组件")
        
        protocol_action_layout = QHBoxLayout()
        protocol_action_layout.addWidget(self.add_btn)
        protocol_action_layout.addWidget(self.save_btn)
        protocol_action_layout.addWidget(self.lock_all_btn)
        protocol_action_layout.addWidget(self.unlock_all_btn)
        
        protocol_header.addLayout(protocol_action_layout)
        protocol_layout.addLayout(protocol_header)
        
        # 协议配置文件下拉框和刷新按钮
        protocol_selector_layout = QHBoxLayout()
        protocol_selector_layout.addWidget(QLabel("协议配置文件:"))
        
        self.protocol_combo = QComboBox()
        protocol_selector_layout.addWidget(self.protocol_combo, 1)  # 设置伸展因子为1，使下拉框占据更多空间
        
        self.refresh_protocol_button = QPushButton("刷新")
        protocol_selector_layout.addWidget(self.refresh_protocol_button)
        
        self.new_protocol_button = QPushButton("新建")
        protocol_selector_layout.addWidget(self.new_protocol_button)
        
        protocol_layout.addLayout(protocol_selector_layout)
        
        # 协议测试UI内容区域
        self.protocol_content_area = QWidget()
        protocol_layout.addWidget(self.protocol_content_area, 1)  # 设置伸展因子为1，使内容区域占据更多空间
        
        # 将两个部分添加到分割器
        self.right_splitter.addWidget(services_widget)
        self.right_splitter.addWidget(protocol_widget)
        
        # 设置初始分割比例为各50%
        total_height = self.height()
        self.right_splitter.setSizes([total_height//2, total_height//2])

        # 左侧占85%，右侧占115%，比例约为17:23
        main_layout.addWidget(left_panel, 17)
        main_layout.addWidget(right_panel, 23)

        # 用于存储设备地址和其对应的广播信息UI控件
        self.device_widgets = {}

        # 连接信号
        self.device_tree.itemExpanded.connect(self.on_device_item_expanded)
        self.device_tree.itemCollapsed.connect(self.on_device_item_collapsed)
        self.filter_input.textChanged.connect(self.filter_device_list)
        
        # 协议下拉框信号连接
        self.protocol_combo.currentTextChanged.connect(self.on_protocol_selected)

        # 用于存储特征UUID和对应的控件
        self.characteristic_widgets = {}
        
        # 连接协议相关信号
        self.connect_protocol_signals()

    # 新增信号，用于与控制器通信
    read_characteristic_requested = Signal(str)
    write_characteristic_requested = Signal(str, object)
    start_notification_requested = Signal(str)
    stop_notification_requested = Signal(str)
    
    # 协议测试相关信号
    protocol_selected = Signal(str)  # 当选择协议文件时发射
    protocol_save_requested = Signal()  # 请求保存协议配置
    protocol_new_requested = Signal(str, str)  # 协议名称，文件路径
    add_component_requested = Signal(str, object)  # 组件类型，组件信息
    lock_all_components_requested = Signal(bool)  # true=锁定，false=解锁

    def add_device_to_tree(self, device, advertisement_data):
        """
        将发现的设备添加到设备树中.
        """
        name = device.name or "N/A"
        address = device.address
        rssi = advertisement_data.rssi
        
        # 避免重复添加
        items = self.device_tree.findItems(address, Qt.MatchFlag.MatchExactly, 1)
        if items:
            # 更新RSSI和广播数据
            item = items[0]
            item.setText(0, f"{name} (RSSI: {rssi} dBm)")
            item.setData(0, Qt.ItemDataRole.UserRole, advertisement_data)
            # 应用当前筛选
            self.filter_item(item)
            control_widget = self.device_tree.itemWidget(item, 2)
            return item, control_widget

        item = QTreeWidgetItem(self.device_tree, [f"{name} (RSSI: {rssi} dBm)", address])
        item.setData(0, Qt.ItemDataRole.UserRole, advertisement_data) # 存储广播数据
        
        # 添加设备控制按钮
        control_widget = DeviceControlWidget(address)
        self.device_tree.setItemWidget(item, 2, control_widget)
        
        # 添加一个空的子项, 以确保父项是可展开的
        QTreeWidgetItem(item)
        
        # 应用当前筛选
        self.filter_item(item)
        
        return item, control_widget

    def filter_device_list(self):
        """
        根据筛选输入框的内容, 筛选设备列表.
        """
        for i in range(self.device_tree.topLevelItemCount()):
            item = self.device_tree.topLevelItem(i)
            self.filter_item(item)
            
    def filter_item(self, item):
        """
        根据筛选文本决定是否显示某个item.
        """
        filter_text = self.filter_input.text().lower()
        device_name = item.text(0).lower() # 设备名称在第一列
        
        # 如果筛选文本为空, 或者设备名称包含筛选文本, 则显示
        if not filter_text or filter_text in device_name:
            item.setHidden(False)
        else:
            item.setHidden(True)

    def update_connection_status(self, status: str, address: str, error_msg: str = ""):
        """
        根据连接状态更新UI.
        """
        # 1. 更新全局状态标签
        if status == "connecting":
            self.status_label.setText(f"正在连接到 {address}...")
        elif status == "connected":
            self.status_label.setText(f"已连接到 {address}")
        elif status == "failed":
            self.status_label.setText(f"连接失败: {address} - {error_msg}") # 显示详细错误
        elif status == "disconnected":
            self.status_label.setText("无蓝牙连接")

        # 2. 更新设备列表中的项目状态
        items = self.device_tree.findItems(address, Qt.MatchFlag.MatchExactly, 1)
        if not items:
            return

        item = items[0]
        control_widget = self.device_tree.itemWidget(item, 2)
        
        is_connected = (status == "connected")

        if isinstance(control_widget, DeviceControlWidget):
            control_widget.connect_button.setEnabled(not is_connected)
            control_widget.disconnect_button.setEnabled(is_connected)
        
        font = item.font(0)
        font.setBold(is_connected)
        item.setFont(0, font)

        if not is_connected:
            self.service_tree.clear() # 断开或连接失败后清空服务列表
            self.characteristic_widgets.clear() # 同时清空特征控件字典


    def display_services(self, services_data):
        """
        在UI上显示服务和特征.
        """
        self.service_tree.clear()
        self.characteristic_widgets.clear()

        for service_uuid, characteristics in services_data.items():
            service_item = QTreeWidgetItem(self.service_tree, ["Service", str(service_uuid), ""]) # 补全属性列
            service_item.setExpanded(True) # 默认展开服务

            for char_data in characteristics:
                properties_str = ', '.join(char_data["properties"])
                char_item = QTreeWidgetItem(service_item, ["Characteristic", char_data["uuid"], properties_str])

                # 为每个特征创建一个子项，用于放置控件
                control_item = QTreeWidgetItem(char_item)
                control_item.setFirstColumnSpanned(True) # 让控件占据整行
                
                control_widget = CharacteristicControlWidget(char_data)
                
                # 连接信号
                control_widget.read_requested.connect(self.read_characteristic_requested)
                control_widget.write_requested.connect(self.write_characteristic_requested)
                control_widget.notify_toggled.connect(self._on_notify_toggled)

                # 存储控件
                self.characteristic_widgets[char_data["uuid"]] = control_widget
                
                self.service_tree.setItemWidget(control_item, 0, control_widget)
        
        # self.service_tree.expandAll() # 注释掉此行, 实现默认折叠

    def _on_notify_toggled(self, char_uuid, checked):
        if checked:
            self.start_notification_requested.emit(char_uuid)
        else:
            self.stop_notification_requested.emit(char_uuid)

    def update_characteristic_value(self, char_uuid, value):
        """更新特征控件显示的值"""
        if char_uuid in self.characteristic_widgets:
            widget = self.characteristic_widgets[char_uuid]
            widget.update_value(value)
            
    def update_sent_data(self, char_uuid, data):
        """更新特征控件显示的发送数据"""
        print(f"MainWindow.update_sent_data: 更新特征 {char_uuid} 的发送数据显示")
        
        if char_uuid not in self.characteristic_widgets:
            print(f"错误: 找不到特征 {char_uuid} 对应的控件")
            return
            
        widget = self.characteristic_widgets[char_uuid]
        # 更新历史记录
        if hasattr(widget, 'sent_data_history_text'):
            # 将发送的数据转换为十六进制字符串
            hex_str = data.hex(' ')
            display_text = f"→ {hex_str}"
            print(f"添加到发送历史: {display_text}")
            # 在历史记录中显示发送的数据
            widget.sent_data_history_text.append(display_text)
        else:
            print(f"错误: 控件没有sent_data_history_text属性")

    def on_device_item_expanded(self, item):
        """当设备项展开时, 创建并显示广播信息表格."""
        address = item.text(1)
        adv_data = item.data(0, Qt.ItemDataRole.UserRole)

        # 获取预先创建的空子项
        details_item = item.child(0)
        
        # 创建并设置用于显示广播数据的widget
        adv_widget = self.create_advertisement_details_widget(adv_data)
        self.device_tree.setItemWidget(details_item, 0, adv_widget)
        # 设置子项的高度以适应内容
        details_item.setSizeHint(0, adv_widget.sizeHint())

        details_item.setFirstColumnSpanned(True) # 让widget占据整行
        
        # 强制QTreeWidget重新计算布局, 解决item大小不刷新的问题
        self.device_tree.doItemsLayout()
        
        self.device_widgets[address] = adv_widget

    def on_device_item_collapsed(self, item):
        """当设备项折叠时, 隐藏广播信息表格."""
        address = item.text(1)
        if address in self.device_widgets:
            details_item = item.child(0)
            # 在移除widget之前,先重置sizeHint
            details_item.setSizeHint(0, QSize(-1, -1))
            self.device_tree.setItemWidget(details_item, 0, None)
            details_item.setFirstColumnSpanned(False)
            del self.device_widgets[address]

    def update_advertisement_data(self, address, adv_data):
        """更新设备列表中的RSSI和展开视图中的广播数据."""
        items = self.device_tree.findItems(address, Qt.MatchFlag.MatchExactly, 1)
        if not items:
            return
        
        item = items[0]
        name = adv_data.local_name or "N/A"
        item.setText(0, f"{name} (RSSI: {adv_data.rssi} dBm)")
        item.setData(0, Qt.ItemDataRole.UserRole, adv_data)

        # 如果设备项是展开的, 则更新其显示的广播信息
        if address in self.device_widgets:
            widget = self.device_widgets[address]
            self.populate_adv_widget(widget, adv_data)
            # 更新高度
            item.child(0).setSizeHint(0, widget.sizeHint())
            # 强制QTreeWidget重新计算布局, 解决item大小不刷新的问题
            self.device_tree.doItemsLayout()


    def create_advertisement_details_widget(self, adv_data):
        """创建一个复合控件, 用于显示详细的广播信息."""
        
        # 主容器和布局
        container = QWidget()
        layout = QVBoxLayout(container)
        layout.setContentsMargins(5, 5, 5, 5)

        # 1. 解析后的数据表格
        table = QTableWidget()
        table.setColumnCount(2)
        table.setHorizontalHeaderLabels(["类型", "值"])
        table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
        table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        table.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        table.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        
        # 2. 原始数据部分
        raw_data_layout = QHBoxLayout()
        raw_data_label = QLabel("原始数据:")
        raw_data_edit = QLineEdit()
        raw_data_edit.setReadOnly(True)
        raw_data_layout.addWidget(raw_data_label)
        raw_data_layout.addWidget(raw_data_edit)

        layout.addWidget(table)
        layout.addLayout(raw_data_layout)
        
        # 将子控件存入容器的属性中, 以便后续访问
        container.setProperty("table", table)
        container.setProperty("raw_data_edit", raw_data_edit)
        
        self.populate_adv_widget(container, adv_data)
        
        return container


    def populate_adv_widget(self, container, adv_data):
        """用广播数据填充复合控件."""
        table = container.property("table")
        raw_data_edit = container.property("raw_data_edit")
        
        table.setRowCount(0) # 清空表格

        all_raw_data = bytearray()

        def add_row(key, value):
            row_position = table.rowCount()
            table.insertRow(row_position)
            table.setItem(row_position, 0, QTableWidgetItem(key))
            table.setItem(row_position, 1, QTableWidgetItem(value))

        # 1. Local Name
        if adv_data.local_name:
            add_row("Local Name", adv_data.local_name)

        # 2. TX Power
        if adv_data.tx_power is not None:
            add_row("TX Power", f"{adv_data.tx_power} dBm")

        # 3. Service UUIDs
        if adv_data.service_uuids:
            # 将所有UUID合并为一个字符串显示
            add_row("Service UUIDs", ', '.join(adv_data.service_uuids))
        
        # 4. Service Data
        if adv_data.service_data:
            for uuid, data in adv_data.service_data.items():
                add_row(f"Service Data ({uuid})", data.hex())
                all_raw_data.extend(data)

        # 5. Manufacturer Data
        if adv_data.manufacturer_data:
            for company_id, data in adv_data.manufacturer_data.items():
                 add_row(f"Manufacturer (0x{company_id:04X})", data.hex())
                 all_raw_data.extend(data)

        # 调整表格高度以适应内容
        table.resizeRowsToContents()
        table_height = table.horizontalHeader().height()
        for i in range(table.rowCount()):
            table_height += table.rowHeight(i)
        table.setFixedHeight(table_height)

        # 设置原始数据
        raw_data_edit.setText(all_raw_data.hex())
        
        # 更新整个容器的高度
        container.adjustSize() 

    def on_protocol_selected(self, protocol_file):
        """当选择协议文件时触发"""
        if protocol_file and protocol_file != "无可用协议文件":
            self.protocol_selected.emit(protocol_file)
    
    def on_add_component_clicked(self):
        """添加按钮点击处理"""
        action = self.sender()
        comp_type = ""
        if action == self.add_btn_action:
            comp_type = "button"
        elif action == self.add_txt_action:
            comp_type = "text_display"
        elif action == self.add_slider_action:
            comp_type = "slider"
        
        if comp_type:
            dialog = AddComponentDialog(comp_type, self)
            if dialog.exec():
                comp_info = dialog.get_component_info()
                self.add_component_requested.emit(comp_type, comp_info)
    
    def on_new_protocol_clicked(self):
        """新建协议点击处理"""
        dialog = NewProtocolDialog(self)
        if dialog.exec():
            info = dialog.get_protocol_info()
            if info["name"] and info["path"]:
                self.protocol_new_requested.emit(info["name"], info["path"])
    
    def connect_protocol_signals(self):
        """连接协议测试相关的信号"""
        # 添加组件菜单项
        self.add_btn_action.triggered.connect(self.on_add_component_clicked)
        self.add_txt_action.triggered.connect(self.on_add_component_clicked)
        self.add_slider_action.triggered.connect(self.on_add_component_clicked)
        
        # 保存按钮
        self.save_btn.clicked.connect(self.protocol_save_requested)
        
        # 锁定/解锁所有按钮
        self.lock_all_btn.clicked.connect(lambda: self.lock_all_components_requested.emit(True))
        self.unlock_all_btn.clicked.connect(lambda: self.lock_all_components_requested.emit(False))
        
        # 新建协议按钮
        self.new_protocol_button.clicked.connect(self.on_new_protocol_clicked)
        